<?php
/**
 * 日時計算モジュール
 */
require_once dirname(__FILE__) . '/Math.php';

/**
 * 日時計算クラス
 * Enter description here ...
 * @author admin
 *
 */
class DateMath extends Math
{
	/** 一日の秒数 */
	const DAYSEC = 86400;
	/** 一時間の秒数 */
	const HOURSEC = 3600;
	/** 一分の秒数 */
	const MINUTESEC = 60;

	/** キー：年 */
	const KEY_YEAR = 'year';
	/** キー：月 */
	const KEY_MONTH = 'month';
	/** キー：日 */
	const KEY_DAY = 'day';
	/** キー：時 */
	const KEY_HOUR = 'hour';
	/** キー：分 */
	const KEY_MINUTE = 'minute';
	/** キー：ユニックスタイム */
	const KEY_UNIXTIME = 'unixtime';

	/** 処理結果バッファ：年 */
	protected $_iYear;
	/** 処理結果バッファ：月 */
	protected $_iMonth;
	/** 処理結果バッファ：日 */
	protected  $_iDay;
	/** 処理結果バッファ：時 */
	protected  $_iHour;
	/** 処理結果バッファ：分 */
	protected $_iMinute;
	/** 処理結果バッファ：ユニックスタイム */
	protected $_uTime;

	/** @var 待機状態 */
	public $bCondition;
	/** 処理結果：年 */
	public $iResultYear;
	/** 処理結果：月 */
	public $iResultMonth;
	/** 処理結果：日 */
	public $iResultDay;
	/** 処理結果：時 */
	public $iResultHour;
	/** 処理結果：分 */
	public $iResultMinute;
	/** 処理結果：ユニックスタイムスタンプ */
	public $iResultUTimi;
	/** 処理結果：ＹＹＹＹＭＭＤＤＨＨＭＭ */
	public $sYMDHM;

	/** 和文曜日 */
	public $aryDayOfWeekJp = array("日","月","火","水","木","金","土");

	/**
	 * コンストラクタ
	 * Enter description here ...
	 * @param string $sDateTime 基準日時
	 * @param unknown_type $bUT ユニックスタイムスタンプ使用フラグ
	 */
	public function __construct($sDateTime='', $bUT=FALSE)
	{
		$this->bCondition = FALSE;

		if ( $sDateTime === '' ) {
			$this->_uTime = time();
		}

		if ( $bUT === FALSE ) {
			$this->_divideYMDHM($sDateTime);
		} else {
			$this->_uTime = $sDateTime;
			$this->_divideYMDHM($sDateTime, $bUT);
		}
	}

	/**
	 * 指定日数後の日付を設定する
	 * Enter description here ...
	 * @param unknown_type $iDays
	 */
	public function getDateAfterDays($iDays)
	{
		$uTimeDays = self::DAYSEC * $iDays;
		$uTimeDays = $uTimeDays + $this->_uTime;
		$this->_createResult($uTimeDays);
	}

	/**
	 * 指定時看護の日付を設定する
	 * Enter description here ...
	 * @param unknown_type $iHours
	 */
	public function getDateAfterHours($iHours)
	{
		$uTimeHours = self::HOURSEC * $iHours;
		$uTimeHours = $uTimeHours + $this->_uTime;
		$this->_createResult($uTimeHours);
	}

	/**
	 * 指定分後の日時を求める
	 * Enter description here ...
	 * @param unknown_type $iMinutes
	 */
	public function getDateAfterMinutes($iMinutes)
	{
		$uTimeMinute = self::MINUTESEC * $iMinutes;
		$uTimeMinute = $uTimeMinute + $this->_uTime;
		$this->_createResult($uTimeMinute);
	}

	/**
	 * 指定日数前の日時を求める
	 * Enter description here ...
	 * @param string $sDateTime
	 * @param boolern $bUT
	 */
	public function getPastDay($sDateTime, $bUT=FALSE)
	{
		$aryDateTime = $this->_getDivideYMDHM($sDateTime, $bUT);
		$iPastDays = $this->_compareDate(
			$this->_iYear,
			$this->_iMonth,
			$this->_iDay,
			$aryDateTime[self::KEY_YEAR],
			$aryDateTime[self::KEY_MONTH],
			$aryDateTime[self::KEY_DAY]
		);
		return $iPastDays;
	}

	public function getYYYYMM_afterMonth($iMonths)
	{
		$iYears = floor($iMonths / 12);
		if ( $iYears < 0 ) {
			$iYears ++;
		}
		$iResultYear = $this->_iYear + $iYears;
		$iSumMonths = $iMonths % 12;
		$iResultMonth = $this->_iMonth + $iSumMonths;
		if ( $iSumMonths > 0) {
			if ( $iResultMonth > 12 ) {
				$iResultYear ++;
				$iResultMonth = $iResultMonth - 12;
			}
		} else if ( $iSumMonths < 0 ) {
			if ( $iResultMonth <= 0 ) {
				$iResultYear --;
				$iResultMonth = $iResultMonth + 12;
			}
		}
		$sMonth = sprintf('%02d', $iResultMonth);
		return $iResultYear.$sMonth;
	}

	/**
	 * 指定時間前の日時を求める
	 * Enter description here ...
	 * @param string $sDateTime
	 * @param boolean $bUT
	 */
	public function getPastHourMinute($sDateTime, $bUT = FALSE)
	{
		$aryDateTime = $this->_getDivideYMDHM($sDateTime, $bUT);
		if ( $aryDateTime === FALSE){
			return FALSE;
		}
		$iSec = $aryDateTime[self::KEY_UNIXTIME] - $this->_uTime;
		$iHour = (int)($iSec / self::HOURSEC);
		$iSec = $iSec % self::HOURSEC;
		$iMinute = (int)($iSec / self::MINUTESEC);

		$aryResult = array(
			self::KEY_HOUR=>$iHour, self::KEY_MINUTE=>$iMinute);
			return $aryResult;
	}

	/**
	 * 指定日の曜日を求める
	 * Enter description here ...
	 * @param string $sDateTime
	 * @param string $bUT
	 */
	public function getDayOfWeek($sDateTime='', $bUT=FALSE)
	{
		$uTime = $this->_uTime;
		if ( $sDateTime != '' ) {
			$aryDateTime = $this->_getDivideYMDHM($sDateTime, $bUT);
			$uTime = $aryDateTime[self::KEY_UNIXTIME];
		}
		$dayOfWeek = date("w", $uTime);
		return $dayOfWeek;
	}

	/**
	 * 年月を指定して月末日を求める関数
	 * $year 年
	 * $month 月
	 */
	public static function getMonthEndDay($year, $month) {
		//mktime関数で日付を0にすると前月の末日を指定したことになります
		//$month + 1 をしていますが、結果13月のような値になっても自動で補正されます
		$dt = mktime(0, 0, 0, $month + 1, 0, $year);
		return date("d", $dt);
	}

	/**
	 * ??????
	 * Enter description here ...
	 * @param unknown_type $sYMDHM
	 */
	public function setYMDHM2UT($sYMDHM)
	{
		$this->bCondition = TRUE;
	}

	protected function _getDivideYMDHM($sDateTime, $bUT=FALSE)
	{
		if ( $bUT === FALSE ) {
			if ( !is_numeric($sDateTime)) {
				return FALSE;
			}
			if ( strlen($sDateTime) != strlen('yyyymmddhhmm')) {
				return FALSE;
			}
			$uTime
			= mktime(
				substr($sDateTime, 8, 2),
				substr($sDateTime, 10, 2),
				0,
				substr($sDateTime, 4, 2),
				substr($sDateTime, 6, 2),
				substr($sDateTime, 0, 4)
			);
			$aryResult = array(
				self::KEY_YEAR=>substr($sDateTime, 0, 4),
				self::KEY_MONTH=>substr($sDateTime, 4, 2),
				self::KEY_DAY=>substr($sDateTime, 6,2),
				self::KEY_HOUR=>substr($sDateTime, 8, 2),
				self::KEY_MINUTE=>substr($sDateTime, 10, 2),
				self::KEY_UNIXTIME=>$uTime
			);
			$bCheckDate
			= checkdate(
				$aryResult[self::KEY_MONTH],
				$aryResult[self::KEY_DAY],
				$aryResult[self::KEY_YEAR]
			);
			if ( !$bCheckDate ) {
				return FALSE;
			}
			if ( ($aryResult[self::KEY_HOUR] < 0 )
					OR
				($aryResult[self::KEY_HOUR] > 23)
			) {
				return FALSE;
			}
		} else {
			$aryResult = array(
				self::KEY_YEAR=>date('Y', $sDateTime),
				self::KEY_MONTH=>date('m', $sDateTime),
				self::KEY_DAY=>date('d', $sDateTime),
				self::KEY_HOUR=>date('H', $sDateTime),
				self::KEY_MINUTE=>date('i', $sDateTime),
				self::KEY_UNIXTIME=>$sDateTime
			);
		}
		return $aryResult;
	}

	protected function _divideYMDHM($sDateTime, $bUT=FALSE)
	{
		if ( $bUT === FALSE ) {
			if ( !is_numeric($sDateTime)) {
				$this->bCondition = FALSE;
				return FALSE;
			}
			if ( strlen($sDateTime) != strlen('yyyymmddhhmm')) {
				$this->bCondition = FALSE;
				return FALSE;
			}
			$this->_iYear = substr($sDateTime, 0, 4);
			$this->_iMonth = substr($sDateTime, 4, 2);
			$this->_iDay = substr($sDateTime, 6, 2);
			$this->_iHour = substr($sDateTime, 8, 2);
			$this->_iMinute = substr($sDateTime, 10, 2);
			if ( !$this->_validYMDHM() ) {
				return FALSE;
			}
			$this->_uTime
			= mktime($this->_iHour, $this->_iMinute,
							0, $this->_iMonth, $this->_iDay, $this->_iYear);
		} else {
			$this->_iYear = date('Y', $sDateTime);
			$this->_iMonth  = date('m', $sDateTime);
			$this->_iDay  = date('d', $sDateTime);
			$this->_iHour  = date('H', $sDateTime);
			$this->_iMinute = date('i', $sDateTime);
		}
		$this->bCondition = TRUE;
		$this->iResultYear =$this->_iYear;
		$this->iResultMonth = $this->_iMonth;
		$this->iResultDay = $this->_iDay;
		$this->iResultHour = $this->_iHour;
		$this->iResultMinute = $this->_iMinute;
	}

	protected function _createResult($uTime)
	{
		$this->iResultYear = date('Y', $uTime);
		$this->iResultMonth = date('m', $uTime);
		$this->iResultDay = date('d', $uTime);
		$this->iResultHour = date('H', $uTime);
		$this->iResultMinute = date('i', $uTime);
		$this->sYMDHM
		= $this->iResultYear
		. $this->iResultMonth
		. $this->iResultDay
		. $this->iResultHour
		. $this->iResultMinute;
	}

	protected function  _validYMDHM()
	{
		if (!checkdate($this->_iMonth, $this->_iDay, $this->_iYear)){
			return FALSE;
		}
		if ( ( $this->_iHour < 0 ) OR ($this->_iHour > 23)) {
			return FALSE;
		}
		if (( $this->_iMinute) < 0  OR ($this->_iMinute > 59)) {
			return FALSE;
		}
		return TRUE;
	}

	protected function _compareDate
	($year1, $month1, $day1, $year2, $month2, $day2)
	{
		$dt1 = mktime(0,0,0, $month1, $day1, $year1);
		$dt2 = mktime(0,0,0, $month2, $day2, $year2);
		$diff = $dt2 - $dt1;
		$diffDay = $diff / self::DAYSEC;
		return $diffDay;
	}
	
	static public function warekiToYear($strYmd, $encoding = NULL)
	{
		try {
			$judgeNengos = array(
					'明治' => array('nengo' => '明治', 'start' => '1868-09-08', 'end' => '1912-07-29', 'baseYear' => 1867),
					'大正' => array('nengo' => '大正', 'start' => '1912-07-30', 'end' => '1926-12-24', 'baseYear' => 1911),
					'昭和' => array('nengo' => '昭和', 'start' => '1926-12-25', 'end' => '1989-01-07', 'baseYear' => 1925),
					'平成' => array('nengo' => '平成', 'start' => '1989-01-08', 'end' => '9999-12-31', 'baseYear' => 1988)
			);
			$judgeNengos['m'] =& $judgeNengos['明治'];
			$judgeNengos['t'] =& $judgeNengos['大正'];
			$judgeNengos['s'] =& $judgeNengos['昭和'];
			$judgeNengos['h'] =& $judgeNengos['平成'];
	
			// エンコーディング指定なし
			if ($encoding === NULL) {
				// 内部エンコーディングを使用
				$encoding = mb_internal_encoding();
			}
	
			// 元年を1年に変換
			$strYmd = str_replace('元', '1', $strYmd);
	
			// 空白文字類を半角スペースに変換
			$strYmd = preg_replace('/\s/is', ' ', $strYmd);
			if ($strYmd === FALSE) {
				throw new Exception('空白文字類の置換に失敗しました。');
			}
	
			// a：「全角」英数字を「半角」に変換
			// s：「全角」スペースを「半角」に変換
			$strYmd = mb_convert_kana($strYmd, 'as', $encoding);
	
			// 大文字を小文字に変換
			$strYmd = strtolower($strYmd);
	
			// 年号部分が存在しない場合
			$matches = NULL;
			if (! preg_match('/明治|大正|昭和|平成|m|t|s|h/is', $strYmd, $matches)) {
				throw new Exception('未定義の年号です。');
			}
			// 年号
			$nengo = $matches[0];
			if (! array_key_exists($nengo, $judgeNengos)) {
				throw new Exception('不正な年号です。');
			}
	
			// 年号部分を削除
			$strYmd = str_replace($nengo, '', $strYmd);
			// 数字以外を半角スペースに変換
			$strYmd = preg_replace('/[^\d]+/', ' ', $strYmd);
			if ($strYmd === FALSE) {
				throw new Exception('数字以外の置換に失敗しました。');
			}
	
			list($wareki, $month, $day) = sscanf($strYmd, '%s %s %s');
			// 2桁0埋め
			$month = sprintf('%02d', $month);
			$day = sprintf('%02d', $day);
	
			if (! preg_match('/\d{1,2}/', $wareki)) {
				throw new Exception('不正な和歴です。');
			}
	
			if ($wareki <= 0) {
				throw new Exception('年は1以上を指定してください。');
			}
	
			$judgeNengo = $judgeNengos[$nengo];
			// 西暦変換
			$year = $wareki + $judgeNengo['baseYear'];
	
			$ymd = "$year-$month-$day";
			$bMatch = FALSE;
			foreach ($judgeNengos as $nengos) {
				if ($nengos['start'] <= $ymd && $ymd <= $nengos['end']) {
					$bMatch = TRUE;
					break;
				}
			}
			if (! $bMatch) {
				throw new Exception('範囲外の年月日です。');
			}
			// 日付の妥当性を検証
			if (! checkdate($month, $day, $year)) {
				throw new Exception('不正な年月日です。');
			}
	
			return $ymd;
		} catch (Exception $e) {
			return $e->getMessage();
		}
	}
	
	//--------------------------------------------------------------------------
	/**
	 * 和暦日付を西暦日付に変換する
	 *
	 * @param string nengo 年号コード
	 * @param string year 年(和暦年 yy)
	 * @param string month 月(mm)
	 * @param string day 日(dd)
	 * @return string 西暦日付(YYYY/MM/DD)
	 */
	const NENGO_MEIJI = '明治';
	const NENGO_TAISHOU = '大正';
	const NENGO_SHOUWA = '昭和';
	const NENGO_HEISEI = '平成';
	//--------------------------------------------------------------------------
	public static function convertSeireki1($nengo, $year, $month=1, $day=1)
	{
	
		// 西暦に変換
		$sYear = "";
		if ( $nengo == self::NENGO_MEIJI ) {
			$sYear = 1867 + $year;
		}
		else if ( $nengo == self::NENGO_TAISHOU ) {
			$sYear = 1911 + $year;
		}
		else if ( $nengo == self::NENGO_SHOUWA ) {
			$sYear = 1925 + $year;
		}
		else if ( $nengo == self::NENGO_HEISEI ) {
			$sYear = 1988 + $year;
		}
	
		//年月日を文字列として結合
		$month = sprintf("%02d",$month);
		$day = sprintf("%02d",$day);
		$ymd = "{$sYear}/{$month}/{$day}";
	
		$aryResult = array(
				self::KEY_YEAR=>$sYear,
				self::KEY_MONTH=>$month,
				self::KEY_DAY=>$day,
				'y/m/d'=>$ymd,
		);
		return $aryResult;
	}
	
	/**
	 * 誕生日から年齢を算出す
	 * @param unknown $y
	 * @param unknown $m
	 * @param unknown $d
	 */
	public static function convBirthToAge($y,$m,$d)
	{
		$now = date('Ymd');
		$m = sprintf('%02d', $m);
		$d = sprintf('%02d', $d);
		$birthday = $y.$m.$d;
		$age = floor(($now-$birthday)/10000);
		return $age;
	}
}